package blackstar.plugin.ui;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import blackstar.plugin.ui.vo.DeadLockInfo;
import blackstar.plugin.ui.vo.ThreadCPUInfo;
import blackstar.plugin.ui.vo.ThreadInformation;
import blackstar.plugin.ui.vo.ThreadTraceInformation;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * @author RayWoo (ayufox@gmail.com)
 * 
 */
public class ThreadServlet extends HttpServlet
{
	private final static Log LOGGER = LogFactory.getLog(ThreadServlet.class);

	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException
	{
		String uri = request.getRequestURI();
		if (!"/".equals(request.getContextPath()))
		{
			uri = uri.substring(request.getContextPath().length());
		}

		LOGGER.info("request:" + uri);
		String[] pathArray = uri.substring(1).split("/");

		if (pathArray.length == 1)
		{
			response.sendError(404);
		} else if (pathArray.length == 2)
		{
			String jvmid = pathArray[1];
			printThreads(request, response, jvmid);
		} else if (pathArray.length == 3)
		{
			String jvmid = pathArray[1];
			String type = pathArray[2];
			if ("deadlock".equals(type))
			{
				printDeadLock(request, response, jvmid);
			} else
			{
				response.sendError(404);
			}
		} else if (pathArray.length == 4)
		{
			String jvmid = pathArray[1];
			String threadIdStr = pathArray[2];
			try
			{
				long threadId = Long.parseLong(threadIdStr);
				String type = pathArray[3];
				if ("trace".equals(type))
				{
					printTrace(request, response, jvmid, threadId);
				}  else if ("cpu".equals(type))
				{
					threadCPU(request, response, jvmid, threadId);
				} else if ("cpu_json".equals(type))
				{
					threadCPUJson(request, response, jvmid, threadId);
				}else
				{
					response.sendError(404);
				}
			} catch (NumberFormatException e)
			{
				LOGGER.info("error threadId [" + threadIdStr + "]", e);
				response.sendError(404);
			}
		} else
		{
			response.sendError(404);
		}
	}

	private void printDeadLock(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		List<DeadLockInfo> deadLockThreads = JMXProxyUtils.listDeadLocks(jvmid);
		if (deadLockThreads == null)
		{
			LOGGER.info("error jvmid [" + jvmid + "]");
			response.sendError(404);
		} else
		{

			PrintWriter writer = response.getWriter();
			writer.print("{");
			writer.print("count :");
			writer.print(deadLockThreads.size());
			writer.print(",list :[");
			int index = 0;
			NumberFormat format = NumberFormat.getInstance();
			format.setMaximumFractionDigits(3);
			format.setGroupingUsed(false);
			for (DeadLockInfo thread : deadLockThreads)
			{
				if (index > 0)
				{
					writer.print(",");
				}
				writer.print("{");
				writer.print("threadId:");
				writer.print(thread.getThreadId());
				writer.print(",threadName:'");
				writer.print(thread.getThreadName());
				writer.print("',state:'");
				writer.print(thread.getState());
				writer.print("',lockOwnerId : ");
				writer.print(thread.getLockOwnerId());
				writer.print("}");
				index++;
			}
			writer.print("]}");
			writer.flush();

		}
	}

	private void printTrace(HttpServletRequest request,
			HttpServletResponse response, String jvmid, long threadId)
			throws IOException
	{
		ThreadTraceInformation trace = JMXProxyUtils.getThreadTrace(jvmid,
				threadId);
		if (trace == null)
		{
			LOGGER.info("error jvmid [" + jvmid + "] or threadId[" + threadId
					+ "]");
			response.sendError(404);
		} else
		{

			PrintWriter writer = response.getWriter();
			writer.print("{");
			writer.print("threadName :'");
			writer.print(trace.getThreadName());
			writer.print("(");
			writer.print(trace.getThreadId());
			writer.print(")");
			writer.print("', lockName :'");
			writer.print(trace.getLockName());
			if (trace.getLockOwnerName() != null)
			{
				writer.print("(");
				writer.print(trace.getLockOwnerName());
				writer.print(")");
			}
			writer.print("', state :'");
			writer.print(trace.getState());
			writer.print("', blockedCount :");
			writer.print(trace.getBlockedCount());
			writer.print(", waitedCount :");
			writer.print(trace.getWaitedCount());
			writer.print(",stacktrace :[");
			int index = 0;
			for (String str : trace.getTrades())
			{
				if (index > 0)
				{
					writer.print(",");
				}
				writer.print("'");
				writer.print(str);
				writer.print("'");
				index++;
			}
			writer.print("]}");
			writer.flush();
		}

	}

	private void printThreads(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		List<ThreadInformation> threads = JMXProxyUtils.listThreadInofs(jvmid);
		if (threads == null)
		{
			LOGGER.info("error jvmid [" + jvmid + "]");
			response.sendError(404);
		} else
		{

			PrintWriter writer = response.getWriter();
			writer.print("{");
			writer.print("count :");
			writer.print(threads.size());
			writer.print(",list :[");
			int index = 0;
			NumberFormat format = NumberFormat.getInstance();
			format.setMaximumFractionDigits(3);
			format.setGroupingUsed(false);
			for (ThreadInformation thread : threads)
			{
				if (index > 0)
				{
					writer.print(",");
				}
				writer.print("{");
				writer.print("threadId:");
				writer.print(thread.getThreadId());
				writer.print(",threadName:'");
				writer.print(thread.getThreadName());
				writer.print("',blockedTime:");
				writer.print(format
						.format((double) thread.getBlockedTime() / 1000000));
				writer.print(",blockedCount:");
				writer.print(thread.getBlockedCount());
				writer.print(",waitedTime:");
				writer.print(format
						.format((double) thread.getWaitedTime() / 1000000));
				writer.print(",waitedCount:");
				writer.print(thread.getWaitedCount());
				writer.print(",threadCpuTime:");
				writer.print(format
						.format((double) thread.getThreadCpuTime() / 1000000));
				writer.print(",cpuTimePercent:");
				writer.print(thread.getCpuTimePercent());
				writer.print(",state:'");
				writer.print(thread.getState());
				writer.print("'}");
				index++;
			}
			writer.print("]}");
			writer.flush();
		}
	}

	private void threadCPU(HttpServletRequest request,
			HttpServletResponse response, String jvmid, long threadId)
			throws IOException
	{
		try
		{
			ThreadCPUInfo result = JMXProxyUtils.getThreadCPUInfo(jvmid,
					threadId);
			if (result == null)
			{
				LOGGER.info("error jvmid [" + jvmid + "] or threadId["
						+ threadId + "]");
				response.sendError(404);
			} else
			{
				try
				{
					String title = "<a href='/threading/" + jvmid
							+ "'>Threading["
							+ JMXProxyUtils.getJMXProxyId(jvmid)
							+ "]</a>&nbsp;&nbsp;<<&nbsp;&nbsp;" + "Thread["
							+ threadId + "] CPU";

					Map root = new HashMap();
					root.put("contextPath", request.getContextPath());
					root.put("title", title);
					root.put("threadName", JMXProxyUtils.getThreadName(jvmid,
							threadId));
					root.put("threadCPU", new Long(result.getThreadCPU())
							.toString());
					root.put("systemCPU", new Long(result.getSystemCPU())
							.toString());
					root.put("processCPU", new Long(result.getProcessCPU())
							.toString());
					Configuration cfg = new Configuration();
					cfg.setClassForTemplateLoading(ThreadServlet.class,
							"/template");
					cfg.setObjectWrapper(new DefaultObjectWrapper());

					PrintWriter writer = response.getWriter();

					Template temp = cfg.getTemplate("threading_cpu.html");
					temp.process(root, writer);
					writer.flush();
				} catch (Exception e)
				{
					LOGGER.error("error when gen result", e);

					response.sendError(505);
				}

			}
		} catch (Exception e)
		{
			LOGGER.info("Error Contact with JVM[" + jvmid + "]", e);
			PrintWriter writer = response.getWriter();
			writer
					.println("JMX Connect failed, Retry or Refresh from JConsole.");
			writer.flush();
		}
	}

	private void threadCPUJson(HttpServletRequest request,
			HttpServletResponse response, String jvmid, long threadId)
			throws IOException
	{
		try
		{
			ThreadCPUInfo result = JMXProxyUtils.getThreadCPUInfo(jvmid,
					threadId);
			if (result == null)
			{
				LOGGER.info("error jvmid [" + jvmid + "] or threadId["
						+ threadId + "]");
				response.sendError(404);
			} else
			{
				response.setContentType("application/json;charset=UTF-8");
				PrintWriter writer = response.getWriter();
				writer.println("{threadCPU:" + result.getThreadCPU()
						+ ",systemCPU:" + result.getSystemCPU()
						+ ",processCPU:" + result.getProcessCPU() + "}");
				writer.flush();
			}
		} catch (Exception e)
		{
			LOGGER.info("Error Contact with JVM[" + jvmid + "]", e);
			PrintWriter writer = response.getWriter();
			writer
					.println("JMX Connect failed, Retry or Refresh from JConsole.");
			writer.flush();
		}
	}

}
